#pragma once
#include "TCPClient.h"

struct OverlappedData;
class TCPServer : public Dispatcher
{
public:
	TCPServer();
	virtual ~TCPServer();

	bool Initialize(int port);

	void Terminate();

	void InsertContext(OverlappedData* data);

	void RemoveContext(OverlappedData* data);

	template <class _Fx, class _Types>
	void RegisterClientConnectedCallback(_Fx&& _Func, _Types&& _User) {
		onClientConnectedEvent = std::bind(_STD forward<_Fx>(_Func), _User, std::placeholders::_1);
	}

	template <class _Fx>
	void RegisterClientConnectedCallback(_Fx&& _Func) {
		onClientConnectedEvent = std::bind(_STD forward<_Fx>(_Func), std::placeholders::_1);
	}

	void UnregisterClientConnectedCallback() {
		onClientConnectedEvent = nullptr;
	}

	template <class _Fx, class _Types>
	void RegisterClientDisconnectedCallback(_Fx&& _Func, _Types&& _User) {
		onClientDisconnectedEvent = std::bind(_STD forward<_Fx>(_Func), _User, std::placeholders::_1);
	}

	template <class _Fx>
	void RegisterClientDisconnectedCallback(_Fx&& _Func) {
		onClientDisconnectedEvent = std::bind(_STD forward<_Fx>(_Func), std::placeholders::_1);
	}

	void UnregisterClientDisconnectedCallback() {
		onClientDisconnectedEvent = nullptr;
	}

	template <class _Fx, class _Types>
	void RegisterClientReceivedCallback(_Fx&& _Func, _Types&& _User) {
		onClientReceivedEvent = std::bind(_STD forward<_Fx>(_Func), _User, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
	}

	template <class _Fx>
	void RegisterClientReceivedCallback(_Fx&& _Func) {
		onClientReceivedEvent = std::bind(_STD forward<_Fx>(_Func), std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
	}

	void UnregisterClientReceivedCallback() {
		onClientReceivedEvent = nullptr;
	}

	static bool BeginReceive(OverlappedData* data);

protected:

	TCPClient* IncomingConnection();


private:

	static void BeginAccept(TCPServer* server);

	void AcceptThread();

	static unsigned long _stdcall WorkerThread(void* lpParameter);

	void Run(int port);

	void OnClientConnected(OverlappedData* data);

	void OnClientDisconnected(OverlappedData* data);

	void OnClientReceived(OverlappedData* data, char* buffer, int size);

	void OnClientSended(OverlappedData* data, char* buffer, int size);

	void FreeAllContext();

	void ReleaseOpenSignal(bool initSuccess);

public:

	uint64_t ConnectCount = 0;
	uint64_t DisconnectCount = 0;
	uint64_t ReceiveByteCount = 0;
	uint64_t SendByteCount = 0;

	void* user = nullptr;

private:
	std::function<void(TCPClient*)> onClientConnectedEvent;
	std::function<void(TCPClient*)> onClientDisconnectedEvent;
	std::function<void(TCPClient*, char* data, int size)> onClientReceivedEvent;

private:
	std::thread* threadHandle = nullptr;
	void* completionPort = nullptr;
	uint64_t listenSocket = 0;

	std::mutex quitMux;
	std::condition_variable quitCond;

	OverlappedData* lastAllocContext = nullptr;
	std::thread* acceptThreadHandle = nullptr;

	std::unordered_set<OverlappedData*> overlappedDataSet;
	std::mutex overlappedDataSetMux;

	std::mutex openMux;
	std::condition_variable openCond;
	bool initResult = false;

	char localAddr[32];
};

